package com.stone.bypass.proxy.core.support;

import com.stone.bypass.proxy.core.anno.DiversionService;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

/**
 * @Auther: stone
 * @Date: 2021/3/7 17:27
 * @Description:
 */
public class CreateDiversionProxyFactoryBean<T> implements FactoryBean<T>, ApplicationContextAware {

    private Class<T> customInterface;

    private String proxyBeanName;

    private ApplicationContext applicationContext;

    public CreateDiversionProxyFactoryBean(Class<T> customInterface, String proxyBeanName) {
        this.customInterface = customInterface;
        this.proxyBeanName = proxyBeanName;
    }


    @Override
    public T getObject() throws Exception {
        MethodProxyHandler<T> invocationHandler = getProxyHanlder();
        return (T) Proxy
            .newProxyInstance(customInterface.getClassLoader(), new Class[]{this.customInterface},
                invocationHandler);
    }


    private MethodProxyHandler<T> getProxyHanlder() {
        Map<String, Object> proxyMap = getProxyBeansForType(customInterface, proxyBeanName);
        return new MethodProxyHandler<T>(proxyMap, customInterface, applicationContext);
    }


    private Map<String, Object> getProxyBeansForType(Class<T> proxyInterface, String beanName) {
        String[] beanNames = applicationContext.getBeanNamesForType(proxyInterface);
        Map<String, Object> proxyMap = new HashMap<>();
        for (int i = 0; i < beanNames.length; i++) {
            if (beanNames[i] != null && !beanNames[i].equals(beanName)) {
                Object bean = applicationContext.getBean(beanNames[i]);
                Class beanCls = bean.getClass();
                String version = DiversionSelector.VERSION_EMPTY;
                DiversionService diversionService = (DiversionService) beanCls.getAnnotation(DiversionService.class);
                if (diversionService != null) {
                    version = diversionService.version();
                }
                proxyMap.put(version, bean);
            }
        }
        return proxyMap;
    }

    @Override
    public Class<?> getObjectType() {
        return this.customInterface;
    }


    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    public Class<T> getCustomInterface() {
        return customInterface;
    }

    public void setCustomInterface(Class<T> customInterface) {
        this.customInterface = customInterface;
    }

    public String getProxyBeanName() {
        return proxyBeanName;
    }

    public void setProxyBeanName(String proxyBeanName) {
        this.proxyBeanName = proxyBeanName;
    }
}
